<!doctype html>
<title>Credential Manager: get() basics.</title>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script type="module">
import {SmsStatus} from '/gen/third_party/blink/public/mojom/sms/webotp_service.mojom.m.js';
import {AuthenticatorStatus} from '/gen/third_party/blink/public/mojom/webauthn/authenticator.mojom.m.js';
import {MockAuthenticator, MockCredentialManager, MockWebOTPService} from './resources/mock-navigator-credentials.js';
import {assertValidGetCredentialResponse, CABLE_AUTHENTICATION, CABLE_REGISTRATION, deepCopy, GET_CREDENTIAL_OPTIONS, RAW_ID} from './resources/test-inputs.js';

if (document.location.host != "subdomain.example.test:8443") {
  document.location = "https://subdomain.example.test:8443/credentialmanager/credentialscontainer-get-basics.html";
  promise_test(_ => new Promise(_ => {}), "Stall tests on the wrong host.");
}

const mockAuthenticator = new MockAuthenticator;
const mockCredentialManager = new MockCredentialManager;
const mockWebOTPService = new MockWebOTPService;

add_completion_callback(() => {
  mockCredentialManager.reset();
  mockWebOTPService.reset();
});

promise_test(_ => {
  return navigator.credentials.get().then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with no argument.");

promise_test(_ => {
  return navigator.credentials.get({}).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get({}).");

promise_test(_ => {
  return navigator.credentials.get({
    federated: {
      providers: [ 'https://example.com/' ]
    }
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with a valid options including FederatedCredentialRequestOptions.");

promise_test(_ => {
  return navigator.credentials.get({
    password: true,
    unmediated: true
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with a valid options including password and unmediated.");

promise_test(_ => {
  return navigator.credentials.get({
    federated: {
      providers: [ 'https://example.com/' ]
    },
    unmediated: true
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with a valid options including federated and unmediated.");

promise_test(_ => {
  return navigator.credentials.get({
    password: true,
    federated: {
      providers: [ 'https://example.com/' ]
    },
    unmediated: true
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with a valid options including federated, password and unmediated.");

promise_test(_ => {
  return navigator.credentials.get({
    unmediated: true
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with a valid options including unmediated.");

promise_test(_ => {
  return navigator.credentials.get({
    mediation: "silent"
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with a valid options including mediation.");

promise_test(_ => {
  return navigator.credentials.get({
    notValid: 'yay!'
  }).then(r => {
    assert_equals(r, null);
  });
}, "navigator.credentials.get() with an options including an unknown attribute.");

promise_test(_ => {
  var id = "id";
  var password = "pencil";
  var name = "name";
  var icon = "http://example.com/";

  mockCredentialManager.setResponse(id, password, name, icon);

  return navigator.credentials.get({
    password: true
  }).then(r => {
    assert_equals(r.id, id, "id");
    assert_equals(r.password, password, "password");
    assert_equals(r.name, name, "name");
    assert_equals(r.iconURL, icon, "icon");
  });
}, "Verify that the mock returns the values we give it.");

promise_test(_ => {
  mockAuthenticator.setRawId(RAW_ID);
  mockAuthenticator.setDefaultsForSuccessfulGetAssertion();
  return navigator.credentials.get({publicKey : GET_CREDENTIAL_OPTIONS}).then(r => {
      assertValidGetCredentialResponse(r);
  });
}, "Verify that mockAuthenticator returns the values we give it.");

promise_test(_ => {
  var otp = "ABC123";
  mockWebOTPService.setOtp(otp);
  mockWebOTPService.setStatus(SmsStatus.kSuccess);
  return navigator.credentials.get({otp: {transport: ["sms"]}}).then(r => {
    assert_equals(r.type, "otp", "type");
    assert_equals(r.id, "", "id");
    assert_equals(r.code, otp, "code");
  });
}, "Verify that mockWebOTPService returns the values we give it.");

promise_test(t => {
  mockWebOTPService.setStatus(SmsStatus.kCancelled);
  return promise_rejects_dom(t, "AbortError",
    navigator.credentials.get({otp: {transport: ["sms"]}}));
}, "Verify that cancelled status returned by mock is properly handled.");

promise_test(t => {
  mockWebOTPService.setStatus(SmsStatus.kAborted);
  return promise_rejects_dom(t, "AbortError",
    navigator.credentials.get({otp: {transport: ["sms"]}}));
}, "Verify that abort status returned by mock is properly handled.");

promise_test(t => {
  mockWebOTPService.setStatus(SmsStatus.kUnhandledRequest);
  return promise_rejects_dom(t, "InvalidStateError",
    navigator.credentials.get({otp: {transport: ["sms"]}}));
}, "Verify that unhandled request status returned by mock is properly handled.");

promise_test(t => {
  mockWebOTPService.setStatus(SmsStatus.kTimeout);
  return promise_rejects_dom(t, "InvalidStateError",
    navigator.credentials.get({otp: {transport: ["sms"]}}));
}, "Verify that timeout status returned by mock is properly handled.");

promise_test(t => {
  mockWebOTPService.setStatus(SmsStatus.kBackendNotAvailable);
  return promise_rejects_dom(t, "InvalidStateError",
    navigator.credentials.get({otp: {transport: ["sms"]}}));
}, "Verify that backend unavailable status returned by mock is properly handled.");

promise_test(t => {
  mockWebOTPService.setStatus(SmsStatus.kTimeout);
  return promise_rejects_dom(t, "NotSupportedError",
    navigator.credentials.get({otp: {transport: []}}));
}, "Verify that invalid transport is properly handled");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.PENDING_REQUEST);
  return promise_rejects_dom(t, "OperationError",
    navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
}, "Verify that pending request error returned by mock is properly handled.");

promise_test(function (t) {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.UNKNOWN_ERROR);
  return promise_rejects_dom(t, "NotReadableError",
      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
}, "Verify that unknown error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(
      AuthenticatorStatus.NOT_ALLOWED_ERROR);
  return promise_rejects_dom(t, "NotAllowedError",
      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
}, "Verify that not allowed error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(
      AuthenticatorStatus.USER_VERIFICATION_UNSUPPORTED);
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
}, "Verify that user verification unsupported error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(
      AuthenticatorStatus.EMPTY_ALLOW_CREDENTIALS);
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  delete customGetCredentialOptions.allowCredentials;
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.get({ publicKey : customGetCredentialOptions}));
}, "Verify that empty allow credentials error returned by mock is properly handled.");

promise_test(t => {
  mockAuthenticator.setAuthenticatorStatus(AuthenticatorStatus.ABORT_ERROR);
  return promise_rejects_dom(t, "AbortError",
      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS}));
}, "Verify that abort error returned by mock is properly handled.");

promise_test(function(t) {
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  delete customGetCredentialOptions.challenge;
  return promise_rejects_js(t, TypeError,
      navigator.credentials.get({publicKey: customGetCredentialOptions}));
}, "navigator.credentials.get() with missing challenge");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulGetAssertion();
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  delete customGetCredentialOptions.rpId;
  console.log(customGetCredentialOptions);
  return navigator.credentials.get({publicKey: customGetCredentialOptions}).then(r => {
      assertValidGetCredentialResponse(r);
  });
}, "navigator.credentials.get() with missing rpId");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulGetAssertion();
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  delete customGetCredentialOptions.userVerification;
  return navigator.credentials.get({publicKey: customGetCredentialOptions}).then(r => {
      assertValidGetCredentialResponse(r);
  });
}, "navigator.credentials.get() with missing user verification requirement");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulGetAssertion();
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  customGetCredentialOptions.extensions = {cableRegistration: CABLE_REGISTRATION};
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.get({ publicKey : customGetCredentialOptions}));
}, "navigator.credentials.get() with cableRegistration extension not supported");

promise_test(_ => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulGetAssertion();
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  customGetCredentialOptions.extensions = {cableAuthentication: [CABLE_AUTHENTICATION]};
  return navigator.credentials.get({publicKey: customGetCredentialOptions}).then(r => {
      assertValidGetCredentialResponse(r);
  });
}, "navigator.credentials.get() with cableAuthentication extension");

promise_test(t => {
  mockAuthenticator.reset();
  mockAuthenticator.setDefaultsForSuccessfulGetAssertion();
  var customGetCredentialOptions = deepCopy(GET_CREDENTIAL_OPTIONS);
  customGetCredentialOptions.extensions = {googleLegacyAppidSupport: true};
  return promise_rejects_dom(t, "NotSupportedError",
      navigator.credentials.get({ publicKey : customGetCredentialOptions}));
}, "navigator.credentials.get() with googleLegacyAppidSupport extension not supported");

promise_test(t => {
  var authAbortController = new AbortController();
  var authAbortSignal = authAbortController.signal;
  authAbortController.abort()
  return promise_rejects_dom(t, "AbortError",
      navigator.credentials.get({ publicKey : GET_CREDENTIAL_OPTIONS, signal : authAbortSignal}));
}, "navigator.credentials.get() with abort signal.");

</script>
